package vis.app;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

import vis.app.components.PanelCodeDisplay;
import vis.app.components.PanelControl;
import vis.app.components.PanelDisplay;
import vis.app.components.PanelSelectItem;
import vis.app.controller.AppController;
import vis.app.controller.CodeController;
import vis.app.controller.EventControl;
import vis.app.utils.Const;
import vis.items.visualItem.AbstractSketch;
import vis.items.visualItem.VisualSketch;

public class MainApp extends JFrame implements Observer {

	private static final long serialVersionUID = -2569297270741165579L;

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				new MainApp().setVisible(true);
			}
		});
	}

	private PanelControl panelControl;
	private PanelDisplay panelDisplay;
	private PanelSelectItem panelSelectItem;
	private PanelCodeDisplay panelCodeDisplay;

	public MainApp() {
		setupFrame();
		setupPanelComponents();
		setupBorderLayout(getContentPane());
		this.pack();

		processAppInit();
	}

	private void setupFrame() {
		setTitle(Const.APP_TITLE);
		setPreferredSize(new Dimension(Const.APP_WIDTH, Const.APP_HEIGHT));
		setDefaultCloseOperation(EXIT_ON_CLOSE);
	}

	private void setupPanelComponents() {
		panelControl = new PanelControl();
		panelControl.init();
		panelControl.setPreferredSize(new Dimension(0, 50));

		panelDisplay = new PanelDisplay();
		panelDisplay.init();

		panelSelectItem = new PanelSelectItem();
		panelSelectItem.init();
		panelSelectItem.setPreferredSize(new Dimension(300, 0));

		panelCodeDisplay = new PanelCodeDisplay();
		panelCodeDisplay.init();
		panelCodeDisplay.setPreferredSize(new Dimension(300, 0));
	}

	private void setupBorderLayout(Container pane) {
		pane.setLayout(new BorderLayout());

		JTabbedPane tabbedPane = new JTabbedPane();
		pane.add(tabbedPane, BorderLayout.WEST);
		tabbedPane.addTab("SELECT ALGO", panelSelectItem);
		tabbedPane.addTab("DEBUG DATA", panelCodeDisplay);
		pane.add(panelDisplay, BorderLayout.CENTER);
		pane.add(panelControl, BorderLayout.SOUTH);

		// pane.add(panelSelectItem, BorderLayout.WEST);
		// pane.add(panelCodeDisplay, BorderLayout.EAST);
	}

	private void processAppInit() {
		AppController.getInstance().addObserver(this);
		AppController.getInstance().setStateReady();
		addRegisteredItemToTree();

		CodeController.getInstance().addObserver(this);
	}

	private void addRegisteredItemToTree() {
		ArrayList<VisualSketch> registeredItems = new RegistryItem().getRegisteredItems();
		for (VisualSketch item : registeredItems) {
			panelSelectItem.addTreeItem(item.type,
					item.visualItemName, item.sketchFullName);
		}
	}

	@Override
	public void update(Observable o, Object arg) {
		EventControl eventCtr = (EventControl) arg;
		String eventType = eventCtr.actionControl;

		// System.out.println("EVENT: " + eventCtr.toString());

		if (eventType.startsWith("CTRL")) {
			updateControlEvent(eventCtr);
		} else if (eventType.startsWith("JOBS")) {
			updateAnimationJobs(eventCtr);
		}
	}

	private void updateControlEvent(EventControl eventCtr) {
		try {
			String sketchFullName = eventCtr.sketchFullName;

			Class<?> clazz = Class.forName(sketchFullName);
			Constructor<?> ctor = clazz.getConstructor(String.class);

			AbstractSketch sketch = (AbstractSketch) ctor.newInstance(sketchFullName);
			AppController.getInstance().addObserver(sketch);

			processUpdateEvent(eventCtr, sketch);

		} catch (ClassNotFoundException
				| NoSuchMethodException
				| SecurityException
				| InstantiationException
				| IllegalAccessException
				| IllegalArgumentException
				| InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	private void processUpdateEvent(EventControl eventControl, AbstractSketch sketch) {
		String eventType = eventControl.actionControl;
		String visualItemName = eventControl.visualItemName;

		switch (eventType) {
		case AppController.CTRL_INIT:
			panelDisplay.initForSketch(sketch);
			CodeController.getInstance().loadCodeText(
					"/" + visualItemName + ".code");
			panelCodeDisplay.showCode();
			break;
		case AppController.CTRL_RUN:
			panelDisplay.runSketch(sketch);
			break;
		case AppController.CTRL_PAUSE:
			break;
		case AppController.CTRL_RESUME:
			break;
		case AppController.CTRL_STOP:
			break;
		default:
			break;
		}
	}

	private void updateAnimationJobs(EventControl eventCtr) {
		String eventType = eventCtr.actionControl;
		switch (eventType) {
		case CodeController.JOBS_ANIMATION:
			String[] lines = (String[]) eventCtr.getData();
			panelCodeDisplay.highlight(lines);
			break;
		default:
			break;
		}
	}
}
